package com.dxtx.widget.scrolldelete;


import java.util.ArrayList;
import java.util.List;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.LinearInterpolator;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;

import com.dxtx.util.SimpleAnimListener;

/**
 * Created by user on 2016/12/19.
 */
@SuppressLint("NewApi")
public class ScrollDeleteListView extends FrameLayout {
    private static final float DEF_NUM = 5;

    private float aNum = DEF_NUM, count = 0;
    private final float num = 5;
    private int mHeight, mWidth;

    private float mScrollY = 0;
    private float mMaxScrollY = 0, mMinScrollY = 0;

    private boolean interceptTouchEvent = false;
    private boolean disallowIntercept = false;
    private boolean horibleScrollEvent = false;

    private GestureDetector detector;

    /**
     * 减速速率
     */
    private static final float ACC = 10f / 1000;

    private View mScrollChild;
    private int mScrollPosition = -1;

    public ScrollDeleteListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ScrollDeleteListView(Context context) {
        super(context);
        init();
    }

    public void init() {
        // setClipToPadding(false);


        detector = new GestureDetector(getContext(), onGestureListener);
    }

    public float getVisableNum() {
        return aNum;
    }

    public void setVisableNum(float num) {
        this.aNum = num;
    }

    public void setAdapter(BaseAdapter adapter) {
        LayoutParams p = new LayoutParams(-1, -2);
        count = adapter.getCount();
        reBoundByCountChange();

        for (int i = 0; i < count; i++) {
            View view = adapter.getView(i, null, this);
            addView(view, p);
        }
        postAdjustChild();
    }

    private void postAdjustChild() {
        getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeOnGlobalLayoutListener(this);
                adjustChild();
            }
        });
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = getMeasuredHeight();
        mWidth = getMeasuredWidth();
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.setPivotY(0);
            child.setPivotX(child.getMeasuredWidth() / 2f);
        }

        reBoundByCountChange();
    }

    /**
     * @���� ��count�ı�ʱ����ĳЩ����
     * @author px / 2016-3-5
     */
    private void reBoundByCountChange() {
        if (count < num) {
            mMaxScrollY = 0.3f * num;
            mMinScrollY = -count / (count + 1);
        } else {
            mMaxScrollY = count - num + 0.1f * num;
            mMinScrollY = -0.4f * num;
        }
    }

    private void adjustChild() {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            // λ����Ϣת���ɼ򵥵ĳ�0~1�任�ĸ�����,��ʾ���ϵ��·����Yֵ.


            float value = convertVariable(i);

            // �ѱ���Ӧ�õ�yֵ


            float y = layoutY(value);
            // �����ʽ��ʾ��С���ϵ��°�0.8��1�ֲ�


            float scale = 0.8f + y / mHeight * 0.2f;

            // topԽС,���Լ�ԽС


            child.setScaleX(scale);
            child.setScaleY(scale);
            child.setY(y);
        }
    }

    private float lastX, lastY;

    // public boolean onInterceptTouchEvent(MotionEvent e) {


    public void handleInterceptTouchEvent(MotionEvent e) {
        switch (e.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                lastX = e.getX();
                lastY = e.getY();
                break;
            case MotionEvent.ACTION_MOVE:


                if (disallowIntercept) {
                    break;
                }
                if (!interceptTouchEvent && Math.abs(e.getX() - lastX) > 50) {
                    horibleScrollEvent = true;
                    break;
                }
                if (Math.abs(e.getY() - lastY) > 50) {
                    interceptTouchEvent = true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
    }

    private void handleHoribleScroll(MotionEvent e) {
        detector.onTouchEvent(e);
        switch (e.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                // ����������View


                if (getChildCount() == 1) {
                    mScrollPosition = 0;
                    mScrollChild = getChildAt(0);
                    break;
                } else if (getChildCount() == 0) {
                    mScrollPosition = -1;
                    mScrollChild = null;
                    break;
                }
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (child.getY() >= e.getY()) {
                        mScrollPosition = i - 1;
                        mScrollChild = getChildAt(mScrollPosition);
                        break;
                    }
                }
                if (mScrollChild == null) {
                    mScrollPosition = getChildCount() - 1;
                    mScrollChild = getChildAt(mScrollPosition);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (horibleScrollEvent && mScrollChild != null) {
                    //处理是否删除
                    bounceOrFlyOut(mScrollChild);
                }
                break;
            default:
                break;
        }
    }

    public boolean hanScrollTouchEvent(MotionEvent e) {
        detector.onTouchEvent(e);
        switch (e.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (mScrollY > mMaxScrollY || mScrollY < mMinScrollY) {
                    bounce();
                }
                break;
            default:
                break;
        }
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        if ((e.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
            if (animator != null && animator.isRunning()) {
                animator.cancel();
            }
        }
        if (horibleScrollEvent) {
            // flag = super.dispatchTouchEvent(e);


            handleHoribleScroll(e);
        } else if (interceptTouchEvent) {
            hanScrollTouchEvent(e);
        } else {

            // ͬʱ����


            hanScrollTouchEvent(e);
            handleHoribleScroll(e);
            super.dispatchTouchEvent(e);

            // ���������߼�,�����´��Ƿ�����


            handleInterceptTouchEvent(e);
        }

        switch (e.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                interceptTouchEvent = disallowIntercept = horibleScrollEvent = false;
                mScrollChild = null;
                break;
            default:
                if (horibleScrollEvent || interceptTouchEvent) {
                    e.setAction(MotionEvent.ACTION_CANCEL);
                    super.dispatchTouchEvent(e);
                }
                break;
        }

        return true;
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
        this.disallowIntercept = disallowIntercept;
    }

    /**
     * @���� ����
     * @author px / 2016-3-4
     */
    private void bounce() {
        float targetY;
        if (mScrollY < mMinScrollY) {
            targetY = mMinScrollY;
        } else if (mScrollY > mMaxScrollY) {
            targetY = mMaxScrollY;
        } else {
            return;
        }
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
        animator = ValueAnimator.ofFloat(mScrollY, targetY);
        animator.addUpdateListener(bounceListener);
        animator.setDuration(250);
        animator.start();
    }

    /**
     * @���� ���򷴵���ɳ�ȥ
     * @author px / 2016-3-5
     */
    private void bounceOrFlyOut(final View child) {
        final float fromX = child.getX();
        // child������λ��


        float centerX = fromX + child.getWidth() / 2f;
        final boolean delete;
        // 决定是否删除还是回到原来的位置
        final float toX;
        if (centerX < 0) {
            toX = -mWidth;
            delete = true;
        } else if (centerX > mWidth) {
            toX = mWidth;
            delete = true;
        } else {
            toX = 0;
            delete = false;
        }

        final List<View> list;
        final List<Float> fromYs = new ArrayList<Float>();
        final List<Float> toYs = new ArrayList<Float>();
        int f, t;
        if (delete) {
            if (onDeleteChildListener != null) {
                onDeleteChildListener.onDelete(mScrollPosition);
            }
            count--;
            reBoundByCountChange();
            // �ж���Ҫƽ������item


            if (count < num) {
                // item��������,��Ҫ����numʱ,��ȫ��View����


                // item̫��, ͣ����λ�ñ�Ȼ����С����.


                list = calculateTranslatePosition(0, (int) count + 1, mScrollY = mMinScrollY, toYs);
            } else if (mScrollY < count - num + 1) {// �·���������һ��Viewʱ,����ʹ�·���item��������

                f = mScrollPosition + 1;
                t = (int) count + 1;
                // �����������ʵ��scrollֵ����


                list = calculateTranslatePosition(f, t, mScrollY, toYs);
            } else if (mScrollY > 0) {
                // �·�View����ʱʹ���Ϸ���View����,


                // ʵ��Ӧ�õ���mScrollYֵ�任��mScrollY-1


                f = (int) (mScrollY - 1);
                t = mScrollPosition;
                list = calculateTranslatePosition(f, t, --mScrollY, toYs);
            } else {

                throw new RuntimeException("trueNum=" + num + ",   mScrollY=" + ",   mScrollPosition="
                        + mScrollPosition);
            }
            for (int i = 0; i < list.size(); i++) {
                fromYs.add(list.get(i).getY());
            }

        } else {
            list = null;
        }
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
        animator = ValueAnimator.ofFloat(0, 1);
        // ���������500���ڷ������


        long during = (long) (Math.abs(toX - fromX) / mWidth * 1000);
        animator.setDuration(500);
        animator.addListener(new SimpleAnimListener() {

            public void onAnimationCancel(Animator animation) {
                child.setX(toX);
                if (delete) {
                    removeView(child);
                    postAdjustChild();
                }
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                if (delete) {
                    removeView(child);
                    postAdjustChild();
                }
            }
        });
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                Float v = (Float) animation.getAnimatedValue();
                child.setX(v * (toX - fromX) + fromX);

                if (delete) {
                    for (int i = 0; i < list.size(); i++) {

                        View view = list.get(i);
                        float y = v * (toYs.get(i) - fromYs.get(i)) + fromYs.get(i);
                        view.setY(y);
                        // �����ʽ��ʾ��С���ϵ��°�0.8��1�ֲ�


                        float scale = 0.8f + y / mHeight * 0.2f;

                        // topԽС,���Լ�ԽС


                        view.setScaleX(scale);
                        view.setScaleY(scale);
                    }
                }
            }
        });
        animator.start();
    }

    private List<View> calculateTranslatePosition(int from, int to, float scrollY, List<Float> list) {
        List<View> views = new ArrayList<View>();
        for (int i = from; i < to; i++) {
            float j;
            if (i == mScrollPosition) {
                continue;
            } else if (i < mScrollPosition) {
                j = i;
            } else {
                j = i - 1;
            }
            View view = getChildAt(i);

            // λ����Ϣת���ɼ򵥵ĳ�0~1�任�ĸ�����,��ʾ���ϵ��·����Yֵ.


            float value = convertVariable(j);
            float y = layoutY(value);

            if (y < 0) {
                y = 0;
            }
            views.add(view);
            list.add(y);
        }
        return views;
    }

    AnimatorUpdateListener bounceListener = new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mScrollY = (Float) animation.getAnimatedValue();
            adjustChild();
        }
    };

    SimpleOnGestureListener onGestureListener = new SimpleOnGestureListener() {
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (interceptTouchEvent) {
                mScrollY += distanceY / mHeight * 3;
                if (mScrollY < mMinScrollY - 0.2f * num) {
                    mScrollY = mMinScrollY - 0.2f * num;
                } else if (mScrollY > mMaxScrollY + 0.2f * num) {
                    mScrollY = mMaxScrollY + 0.2f * num;
                }
                adjustChild();
            } else if (horibleScrollEvent && mScrollChild != null) {
                mScrollChild.setX(mScrollChild.getX() - distanceX);
            }
            return true;
        }

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (interceptTouchEvent) {
                if (mScrollY > mMaxScrollY || mScrollY < mMinScrollY) {
                    return false;
                }
                if (Math.abs(velocityY) < 3000) {
                    return false;
                }
                // ��һ�����ٶȿ�ʼ���Թ���


                autoScroll(velocityY);
                return true;
            } else if (horibleScrollEvent && mScrollChild != null) {

            }
            return false;
        }

        private void autoScroll(float velocityY) {
            float v = velocityY / mHeight / 1000;// ÿ����mScrollY�ı��ֵ


            long duration = (long) Math.abs(v / ACC * mHeight);

            animator = ValueAnimator.ofFloat(v, 0);
            AnimatorUpdateListener inertiaListener = new MyAnimatorUpdateListener(v, mScrollY);
            animator.addUpdateListener(inertiaListener);
            animator.setInterpolator(new LinearInterpolator());
            animator.setDuration(duration);
            animator.start();
        }

        ;
    };

    private ValueAnimator animator;

    class MyAnimatorUpdateListener implements AnimatorUpdateListener {
        // ���ٶ�


        private float v0;
        // ���Ի�����ʼ��y0;


        private float y0;

        private final float a;

        public MyAnimatorUpdateListener(float v0, float y0) {
            this.v0 = v0;
            this.y0 = y0;
            a = v0 > 0 ? -ACC / mHeight : ACC / mHeight;
            // Log.d("px", "a=" + a);


        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // ���ݹ�ʽ y= 1/2at*t+v0t


            float v = (Float) animation.getAnimatedValue();
            float t = (v - v0) / a;
            float y = -(v + v0) * 0.5f * t + y0;
            // Log.d("px", "t=" + t + ",y=" + y);


            // Log.d("px", "scroll distance-->"+(v + v0) * 0.5f * t);


            if (y > mMaxScrollY) {
                y = mMaxScrollY;
                animator.cancel();
            } else if (y < mMinScrollY) {
                y = mMinScrollY;
                animator.cancel();
            }
            mScrollY = y;
            adjustChild();
        }
    }

    /**
     * @param i λ����Ϣ,�ڼ���item,��Ҳ�������ڵ�2.5��λ��,����С����λ��
     * @return ����һ��ĳitemӦ�ڰٷ�ֵ��λ��,����ܸ߶ȵİٷֱ�,һ������0%��100%������
     * @����
     * @author px / 2016-3-7
     */
    private float convertVariable(float i) {
        float value;
        if (count >= num) {
            value = (i - mScrollY) / num;// ���ֵ��λ����mScrollY��mScrollY+trueNum�����View,��0�𽥱䵽1

        } else {
            value = ((i + 1) / (count + 1) * num - mScrollY) / num;
        }
        return value;
    }

    /**
     * @param value
     * @return
     * @���� ����ٷֱȷֲ�����,����ʹ��y=1-cos(1/2*��x)����,ʹ���漷�ý�,������
     * @author px / 2016-3-7
     */
    private float layoutY(float value) {
        if (value > 1) {
            return mHeight;
        } else if (value < 0) {
            return 0;
        }
        float y = (float) (mHeight - Math.cos(Math.PI / 2f * value) * mHeight);

        return y;
    }

    // 删除item的事件监听


    public interface OnDeleteChildListener {
        void onDelete(int position);
    }

    private OnDeleteChildListener onDeleteChildListener;

    public OnDeleteChildListener getOnDeleteChildListener() {
        return onDeleteChildListener;
    }

    public void setOnDeleteChildListener(OnDeleteChildListener onDeleteChildListener) {
        this.onDeleteChildListener = onDeleteChildListener;
    }

}
